package creator.singleton.lazy;

/**
 * 使用双重循环锁校验实现单例模式，这是一种饿汉式的单例模式
 * 思路：
 * 1.构造私有化，防止创建对象
 * 2.成员变量为自己,使用volatile防止重排序，保证可用性
 * 3.提供静态方法使用对象
 * 4.双重校验锁
 * 5.利用类对象.class当锁
 *
 */
public class DoubleCircleSynchronizedSingleton {

    //防止重排序,用volatile加上内存屏障
    private volatile static DoubleCircleSynchronizedSingleton uniqueInstance;

    //不需要再创建一个对象用作锁作用，使用DoubleCircleSynchronized.class即可
//    private static Object object= new Object();

    private DoubleCircleSynchronizedSingleton(){}

//    public static synchronized DoubleCircleSynchronized getDoubleCircleSynchronized(){ 不在方法上锁,这样可以提高读的效率，因为整个锁只针对写的时候加，读不需要
    public static DoubleCircleSynchronizedSingleton getDoubleCircleSynchronized(){
        if(uniqueInstance == null){
//            synchronized (object){
            //利用DoubleCircleSynchronized.class当锁可以不用在本内创建另外一个对象
            synchronized (DoubleCircleSynchronizedSingleton.class){
                if(uniqueInstance == null){
                    /*
                    *  uniqueInstance = new DoubleCircleSynchronized(); 这段代码其实是分为三步执行：
                    * 1.为 uniqueInstance 分配内存空间
                    * 2.初始化 uniqueInstance
                    * 3.将 uniqueInstance 指向分配的内存地址
                    * 由于 JVM 具有指令重排的特性，执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出现问题，
                    * 但是在多线程环境下会导致一个线程获得还没有初始化的实例。
                    * 例如，线程 T1 执行了 1 和 3，此时 T2 调用 getDoubleCircleSynchronized() 后发现 uniqueInstance 不为空，
                    * 因此返回 uniqueInstance，但此时 uniqueInstance 还未被初始化.
                    * 所以volatile很有必要，保证了JVM按照1,2,3步骤执行
                    * */
                    uniqueInstance = new DoubleCircleSynchronizedSingleton();
                }
            }
        }
        return uniqueInstance;
    }
}
